package com.ruowei.modules.sys.service;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.ruowei.config.Constants;
import com.ruowei.modules.sys.domain.OperateLog;
import com.ruowei.modules.sys.domain.annotation.ZonedDateTimeFormat;
import com.ruowei.modules.sys.domain.enumeration.OperateType;
import com.ruowei.modules.sys.repository.OperateLogRepository;
import com.ruowei.security.SecurityUtils;
import com.ruowei.security.UserModel;
import io.swagger.annotations.ApiModelProperty;
import org.javers.core.Javers;
import org.javers.core.JaversBuilder;
import org.javers.core.diff.Diff;
import org.javers.core.diff.changetype.ObjectRemoved;
import org.javers.core.diff.changetype.ValueChange;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.lang.reflect.Field;
import java.time.Instant;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;

@Service
@Transactional(propagation = Propagation.REQUIRES_NEW)
@Async
public class OperateLogService {

    public static final Logger log = LoggerFactory.getLogger(OperateLogService.class);
    private final OperateLogRepository operateLogRepository;
    private final ObjectMapper objectMapper;
    private final Javers javers = JaversBuilder
        .javers()
        .withNewObjectsSnapshot(true)
        .build();

    public OperateLogService(OperateLogRepository operateLogRepository, ObjectMapper objectMapper) {
        this.operateLogRepository = operateLogRepository;
        this.objectMapper = objectMapper;
    }

    /**
     * 添加新增｜导入｜撤销删除操作日志
     */
    public <T> void addLog(String code, String name, Class<T> tClass) {
        operateLogRepository.save(createOperateLog(code, name, tClass.getName().substring(tClass.getName().lastIndexOf(".") + 1), OperateType.CREATE));
    }

    /**
     * 添加修改档案日志
     *
     * @param originalObject 原值
     * @param newObject      新值
     */
    public <T> void addUpdateLog(String code, String name, T originalObject, T newObject) {
        // 获取修改的values
        Diff diff = javers.compare(originalObject, newObject);
        diff.getChangesByType(ValueChange.class).forEach(valueChange ->
            saveUpdateLog(code, name, originalObject.getClass(), valueChange));
    }

    private <T> void saveUpdateLog(String code, String name, Class<T> tClass, ValueChange valueChange) {
        // 获取修改的field信息
        Field field;
        try {
            field = tClass.getDeclaredField(valueChange.getPropertyName());
        } catch (NoSuchFieldException e) {
            log.info("未找到相应字段，字段名称为：{}", valueChange.getPropertyName());
            return;
        }
        ApiModelProperty apiModelProperty = field.getDeclaredAnnotation(ApiModelProperty.class);
        String originalValue;
        String newValue;
        originalValue = valueChange.getLeft() == null ? "" : valueChange.getLeft().toString();
        newValue = valueChange.getRight() == null ? "" : valueChange.getRight().toString();
        operateLogRepository.save(createOperateLog(code, name, tClass.getName().substring(tClass.getName().lastIndexOf(".") + 1), OperateType.UPDATE)
            .field(apiModelProperty.value())
            .originalValue(originalValue)
            .newValue(newValue));
    }

    private OperateLog createOperateLog(String code, String name, String tableName, OperateType operateType) {
        return new OperateLog()
            .code(code)
            .name(name)
            .tableName(tableName)
            .operator(SecurityUtils.getCurrentUserModel().map(UserModel::getUserName).orElse(Constants.ANONYMOUS_USER))
            .operatorLogin(SecurityUtils.getCurrentUserLogin().orElse(Constants.ANONYMOUS_USER))
            .time(Instant.now())
            .operateType(operateType);
    }
}
